#include "Primitiva.h"

Primitiva::Primitiva()
{
    this->inicializador();
}

Primitiva::Primitiva(size_t d)
{
    // Checar se foi passado um valor maior que zero para "d":
    if (d != 0)
    {
        // Tentar alocar espaço para o vetor de coordenadas:
        this->coordenadas = new (nothrow) TIPO_ELEMENTO[d];
        // Checar se foi possível alocar a memória:
        if (this->coordenadas != NULL)
        {
            // Setar a dimensão da primitiva:
            this->set_dimensao(d);
            // Preencher as coordenadas com zeros:
            (*this)[0];
            return;
        }
    }
    // Se "d" for zero OU não for possível alocar espaço para o vetor de coordenadas,
    // usamos o construtor padrão:
    this->inicializador();
}

// Construtor-cópia:
// (http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom)
// (http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html)
Primitiva::Primitiva(const Primitiva& p)
{
    // Copiar a dimensão de "p" para "this":
    this->set_dimensao(p.get_dimensao());
    // Checar se a dimensão é maior que zero:
    if (this->get_dimensao() != 0)
    {
        // Tentar alocar espaço para o vetor de coordenadas:
        this->coordenadas = new (nothrow) TIPO_ELEMENTO[this->get_dimensao()];
        // Checar se foi possível alocar a memória:
        if (this->coordenadas != NULL)
        {
            // Copiando o vetor "coordenadas" de "p" para "this":
            copy(p.coordenadas,p.coordenadas+(p.get_dimensao()),this->coordenadas);
            return;
        }
    }
    // Se a dimensão for zero OU não for possível alocar espaço para a cópia do vetor de coordenadas,
    // usamos o construtor padrão:
    this->inicializador();
}

Primitiva::Primitiva(const char* entrada)
{
    // Converter a string "entrada" para um vetor de "TIPO_ELEMENTO":
    pair<TIPO_ELEMENTO*,size_t> vetor = string_para_vetor(entrada);
    // Caso "vetor" tenha algum elemento:
    if (vetor.second != 0)
    {
        // Setar a dimensão de "this" para a dimensão de "vetor":
        this->set_dimensao(vetor.second);
        // Setar os elementos de "this" para os elementos de "vetor":
        this->coordenadas = vetor.first;
        return;
    }
    // Caso dê algo errado, usamos o construtor padrão:
    this->inicializador();
}

Primitiva::~Primitiva()
{
    delete[] (this->coordenadas);
}

void Primitiva::inicializador()
{
    this->coordenadas = NULL;
    this->set_dimensao(0);
}

pair<TIPO_ELEMENTO*,size_t> Primitiva::string_para_vetor(const char* entrada)
{
    // Objeto "pair" para usar no retorno da função:
    pair<TIPO_ELEMENTO*,size_t> aux(NULL,0);

    // Ponteiro para os tokens e array de delimitadores:
    char* tokens = NULL;
    char delimitadores[] = " ,";

    // Caso a c-string "entrada" não esteja alocada,
    // retornar o pair "aux" com os valores default:
    if (entrada == NULL) return(aux);

    // Alocar espaço para a cópia da c-string "entrada":
    char* C_string = new (nothrow) char[strlen(entrada)+1];

    // Retornar, caso não seja possível alocar "C_string":
    if (C_string == NULL) return(aux);

    /// Se chegarmos até aqui, conseguimos alocar "C_string"!

    // Copiar "entrada" para "C_string":
    strcpy(C_string,entrada);

    // Vetor auxiliar para os tokens capturados:
    size_t capturados_tam = 0;
    TIPO_ELEMENTO* capturados = new (nothrow) TIPO_ELEMENTO[strlen(entrada)];

    // Checar se foi possível alocar o vetor "capturados":
    if (capturados == NULL)
    {
        // Desalocar "C_string":
        delete[] C_string;
        // Retornar o pair "aux" com os valores default:
        return(aux);
    }

    /// Se chegarmos até aqui, conseguimos alocar "C_string" e "capturados"!
    /// Agora vamos pegar os tokens:

    // Pegar o primeiro token:
    tokens = strtok(C_string,delimitadores);
    while (tokens != NULL)
    {
        // Convertendo o token capturado e colocando no vetor "capturados":
        capturados[capturados_tam] = QString(tokens).toDouble();
        // Incrementando o tamanho do vetor "capturados":
        capturados_tam++;
        // Pegando o próximo token:
        tokens = strtok(NULL,delimitadores);
    }

    // Desalocar "C_string" já que não será mais usada:
    delete[] C_string;

    // Checar se há algum elemento no vetor "capturados":
    if (capturados_tam != 0)// Caso tenhamos algum elemento...
    {
        // Alocar um novo vetor do TAMANHO EXATO de "capturados_tam" no vetor do pair "aux":
        aux.first = new (nothrow) TIPO_ELEMENTO[capturados_tam];
        // Checar se foi possível alocar o novo vetor:
        if (aux.first != NULL)
        {
            // Colocando o tamanho do vetor no pair "aux":
            aux.second = capturados_tam;
            // Copiando os elementos para o vetor do pair "aux":
            copy(capturados,capturados+capturados_tam,aux.first);
        }
    }

    // Desalocar o vetor "capturados":
    delete[] capturados;

    // Retorna o pair "aux":
    return(aux);
}

void Primitiva::set_dimensao(size_t d)
{
    this->dimensao = d;
}

size_t Primitiva::get_dimensao() const
{
    return(this->dimensao);
}

void Primitiva::info() const
{
    cout<<get_nome()<<"this = "<<this<<" / e = "<<(this->coordenadas)<<" / d = "<<this->get_dimensao()<<endl;
}

void Primitiva::imprimir() const
{
    cout<<get_abreviatura()<<"(";
    for(size_t i=0 ; i<(this->get_dimensao()) ; i++)
    {
        cout<<(*this)(i);
        if(i != (this->get_dimensao())-1) cout<<",";
    }
    cout<<")";
}

void trocar_atributos(Primitiva& pri, Primitiva& seg)
{
    // Trocar o valor das dimensões:
    swap(pri.dimensao,seg.dimensao);
    // Trocar as referências das coordenadas:
    swap(pri.coordenadas,seg.coordenadas);
}

Primitiva& Primitiva::operator=(Primitiva p)
{
    trocar_atributos(*this,p);
    return(*this);
}

TIPO_ELEMENTO& Primitiva::operator()(size_t posicao)
{
    // Checar se a posição requisitada é válida:
    if (posicao >= (this->get_dimensao()))
    {
        cout<<get_nome()<<"operator(): posicao invalida "<<posicao<<". Permitido de 0 ate "<<(this->get_dimensao()-1)<<"."<<endl;
        exit(1);
    }
    return(coordenadas[posicao]);
}

const TIPO_ELEMENTO& Primitiva::operator()(size_t posicao) const
{
    // Checar se a posição requisitada é válida:
    if (posicao >= (this->get_dimensao()))
    {
        cout<<get_nome()<<"operator(): posicao invalida "<<posicao<<". Permitido de 0 ate "<<(this->get_dimensao()-1)<<"."<<endl;
        exit(1);
    }
    return(coordenadas[posicao]);
}

void Primitiva::operator[](TIPO_ELEMENTO x)
{
    if (this->get_dimensao() != 0) fill(this->coordenadas,(this->coordenadas)+(this->get_dimensao()),x);
}
